﻿using NCS.IConnect.Core.IoC;
using System;
using System.Data;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Runtime.InteropServices;
using System.Web.Http;
using System.Web.Http.Results;
using Newtonsoft.Json.Serialization;

namespace NCS.IConnect.Core.WebApi
{
    /// <summary>
    /// The base class of all built-in <see cref="ApiController"/>s.
    /// </summary>
    [ComVisible(false)]
    public abstract class ApiControllerBase : ApiController
    {
        private string routePrefix;

        /// <summary>
        /// Gets the route prefix.
        /// </summary>
        /// <value>The route prefix.</value>
        public virtual string RoutePrefix
        {
            get
            {
                if (null != routePrefix)
                {
                    return routePrefix;
                }

                RoutePrefixAttribute attribute = this.GetType().GetCustomAttributes(typeof(RoutePrefixAttribute), false).OfType<RoutePrefixAttribute>().FirstOrDefault();
                if (null == attribute)
                {
                    return routePrefix = "";
                }
                return routePrefix = attribute.Prefix;
            }
        }

        /// <summary>
        /// Gets the service locator.
        /// </summary>
        public IServiceLocator ServiceLocator
        {
            get
            {
                return ServiceLocatorFactory.GetServiceLocator();
            }
        }

        /// <summary>
        /// Creates a <see cref="ResponseMessageResult" /> with the specified value and verion no.
        /// </summary>
        /// <typeparam name="T">The type of content in the entity body. </typeparam>
        /// <param name="statusCode">The HTTP status code for the response message.</param>
        /// <param name="value">The content value to format in the entity body.</param>
        /// <param name="versionNo">The version no which needs to be attached in ETag header.</param>
        /// <returns>The created <see cref="ResponseMessageResult"/></returns>
        protected internal ResponseMessageResult Response<T>(HttpStatusCode statusCode, T value, int versionNo)
        {
            var negotiator = this.Configuration.Services.GetContentNegotiator();
            var negotiationResult = negotiator.Negotiate(typeof(T), this.Request, this.Configuration.Formatters);
            HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
            response.Content = new ObjectContent<T>(value, negotiationResult.Formatter, negotiationResult.MediaType);
            response.Headers.ETag = new EntityTagHeaderValue(string.Format("\"{0}\"", versionNo), true);
            return this.ResponseMessage(response);
        }

        /// <summary>
        /// Creates a <see cref="ResponseMessageResult" /> with an auditable object.
        /// </summary>
        /// <typeparam name="T">The type of content in the entity body. </typeparam>
        /// <param name="statusCode">The HTTP status code for the response message.</param>
        /// <param name="auditableValue">An auditable object with entity body and version no.</param>
        /// <returns>The created <see cref="ResponseMessageResult"/></returns>
        protected internal ResponseMessageResult Response<T>(HttpStatusCode statusCode, Auditable<T> auditableValue)
        {
            return this.Response(statusCode, auditableValue.Entry, auditableValue.AuditInfo.VersionNo);
        }

        /// <summary>
        /// Creates a <see cref="ResponseMessageResult" /> with the specified <see cref="HttpStatusCode"/> and verion no.
        /// </summary>
        /// <param name="versionNo">The version no which needs to be attached in ETag header.</param>
        /// <param name="statusCode">The HTTP status code for the response message.</param>
        /// <returns>The created <see cref="ResponseMessageResult"/></returns>
        protected internal ResponseMessageResult Response(int versionNo, HttpStatusCode statusCode = HttpStatusCode.NoContent)
        {
            HttpResponseMessage response = new HttpResponseMessage(statusCode);
            response.Headers.ETag = new EntityTagHeaderValue(string.Format("\"{0}\"", versionNo), true);
            return this.ResponseMessage(response);
        }

        /// <summary>
        /// Gets the next page link.
        /// </summary>
        /// <param name="totalRecords">The total records.</param>
        /// <param name="pageSize">Size of the page.</param>
        /// <param name="pageIndex">Index of the page.</param>
        /// <returns>The <see cref="Uri" /> linking to next page of result.</returns>
        protected Uri GetNextPageLink(int totalRecords, int pageSize, int pageIndex)
        {
            int totalPages = (totalRecords / pageSize) + (totalRecords % pageSize > 0 ? 1 : 0);
            if (totalPages <= pageIndex)
            {
                return null;
            }

            string url = GetBaseUrl() + "?";
            var query = this.Request.RequestUri.ParseQueryString();
            foreach (string key in query.Keys)
            {
                if (string.Compare(key, UriParameterNames.PageIndex, true) == 0)
                {
                    continue;
                }
                foreach (var value in query.GetValues(key))
                {
                    url += string.Format("{0}={1}&", key, value);
                }
            }
            url += string.Format("{0}={1}", UriParameterNames.PageIndex, pageIndex + 1);
            return new Uri(url);
        }

        /// <summary>
        /// Gets the base URL.
        /// </summary>
        /// <returns>System.String.</returns>
        protected string GetBaseUrl()
        {
            string url = this.Request.RequestUri.AbsoluteUri;
            return url.Substring(0, url.IndexOf("/" + this.RoutePrefix.Trim('/'), StringComparison.OrdinalIgnoreCase) + this.RoutePrefix.Trim('/').Length + 1).ToLower();
        }

        /// <summary>
        /// Tries the parse if match header.
        /// </summary>
        /// <param name="eTag">The e tag.</param>
        /// <param name="exists">if set to <c>true</c> [exists].</param>
        /// <returns><c>true</c> if valid ETag exists in the request header collection, <c>false</c> otherwise.</returns>
        protected bool TryParseIfMatchHeader(out int eTag, out bool exists)
        {
            eTag = -1;
            if (!this.Request.Headers.IfMatch.Any())
            {
                exists = false;
                return false;
            }

            exists = true;
            return int.TryParse(this.Request.Headers.IfMatch.First().Tag.Trim('"'), out eTag);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataSet"></param>
        /// <returns></returns>
        protected internal IHttpActionResult OkForDataSet(DataSet dataSet)
        {
            var negotiator = this.Configuration.Services.GetContentNegotiator();
            var formatters = this.Configuration.Formatters;
            var negotiationResult = negotiator.Negotiate(typeof(DataSet), this.Request, formatters);
            var negotiationResultFormatter =
                negotiationResult.Formatter as System.Net.Http.Formatting.JsonMediaTypeFormatter;
            if (negotiationResultFormatter != null)
            {
                var defaultSerializerSettings = negotiationResultFormatter.SerializerSettings;
                var newSerializerSettings = defaultSerializerSettings.Copy(new DefaultContractResolver());
                var newNegotiationResultFormatter = negotiationResultFormatter.Copy(newSerializerSettings);
                var responseMessage = new HttpResponseMessage(HttpStatusCode.OK)
                {
                    Content = new ObjectContent<DataSet>(dataSet, newNegotiationResultFormatter)
                };
                return ResponseMessage(responseMessage);
            }
            return Ok(dataSet);
        }


    }
}
